package com.jy.api.controller.MarsCall;

import com.jy.api.config.WebSocketUrlUtils;
import com.jy.api.controller.admin.params.LeaseProductParams;
import com.jy.api.dao.*;
import com.jy.api.domain.*;
import com.jy.api.payReq.ALLPayQrCodeResult;
import com.jy.api.payReq.AllPayBaseResult;
import com.jy.api.service.LeaseProductService;
import com.jy.api.service.PaymentService;
import com.jy.api.service.impl.SuiXinFuServiceImpl;
import com.jy.api.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.*;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
@RequestMapping(value = "/v1/call", produces = "application/json;charset=utf-8")
public class LeaseProductOrderController {

    @Autowired
    private LeaseProductOrderDao leaseProductOrderDao;
    @Autowired
    private LeaseProductService leaseProductService;
    @Autowired
    private BillRuleDao billRuleDao;
    @Autowired
    private LeaseProductTypeDao leaseProductTypeDao;

    @Autowired
    private SuiXinFuServiceImpl suiXinFuService;
    @Autowired
    private StorePayDao storePayDao;
    @Autowired
    private RedisSyncLocker redisSyncLocker;
    @Autowired
    private WebSocketUrlUtils urlUtils;
    @Autowired
    WorkShiftDao workShiftDao;
    private JdbcTemplate jdbc;

    @Autowired
    private EmployeeDao employeeDao;
    @Autowired
    private NumDao numDao;
    @Autowired
    private InventoryDao inventoryDao;
    @Autowired
    private ProductDao productDao;
    @Autowired
    private StockTransactionDao stockTransactionDao;

    @Autowired
    public void setJdbc(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    /**
     * 编辑/移除/添加原料
     *
     * @param params
     * @return
     */
    @PostMapping("/clone")
    public ResponseEntity clone(@RequestBody LeaseProductParams params) {
        List<BillRule> billRules = billRuleDao.findAllByStoreIdAndEnabled(params.getStoreId(), true);
        for (BillRule rule : billRules) {
            BillRule billRule = new BillRule();
            BeanUtils.copyProperties(rule, billRule, "id");
            billRule.setStoreId(params.getToStoreId());
            billRuleDao.save(billRule);
        }
        List<LeaseProductType> productTypes = leaseProductTypeDao.findByStoreId(params.getStoreId());
        for (LeaseProductType productType : productTypes) {
            LeaseProductType productType1 = new LeaseProductType();
            BeanUtils.copyProperties(productType, productType1, "id");
            productType1.setStoreId(params.getToStoreId());
            leaseProductTypeDao.save(productType1);
        }
        List<LeaseProduct> products = leaseProductService.findByStoreIdOrderByQueueDesc(params.getStoreId());
        for (LeaseProduct product : products) {
            LeaseProduct product1 = new LeaseProduct();
            BeanUtils.copyProperties(product, product1, "id");
            product1.setStoreId(params.getToStoreId());
            LeaseProductType productType = leaseProductTypeDao.findByStoreIdAndName(params.getToStoreId(), product.getProductTypeName());
            if (productType != null) {
                product1.setProductTypeId(productType.getId());
                product1.setProductTypeName(productType.getName());
            }
            List<LeaseProductExtra> extras = new ArrayList<>();
            for (LeaseProductExtra extra : product.getExtras()) {
                LeaseProductExtra extra1 = new LeaseProductExtra();
                BeanUtils.copyProperties(extra, extra1);
                BillRule oriBillRule = billRuleDao.findOne(extra.getRuleId());
                if (oriBillRule != null) {
                    BillRule billRule = billRuleDao.findByStoreIdAndName(params.getToStoreId(), oriBillRule.getName());
                    if (billRule != null) {
                        extra1.setRuleId(billRule.getId());
                        extra1.setRuleName(billRule.getName());
                    }
                }
                extras.add(extra1);
            }
            product1.setTotalCount(product1.getTotalCount() + product.getSalesCount());
            product1.setNumName(new ArrayList<>());
            product1.getExtras().clear();
            product1.setExtras(extras);
            leaseProductService.save(product1);
        }
        return ResponseEntity.ok("操作成功");
    }

    @PostMapping("/lease/order/list")
    public ResponseEntity getData(@RequestBody LeaseProductOrderParams params) {
        Specification<LeaseProductOrder> netSpec = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(criteriaBuilder.equal(root.get("storeId"), params.getStoreId()));
            if (StringUtils.isNotBlank(params.getMobile())) {
                predicates.add(criteriaBuilder.like(root.get("mobile"), "%" + params.getMobile() + "%"));
            }
            if (StringUtils.isNotBlank(params.getNumName())) {
                predicates.add(criteriaBuilder.like(root.get("numName"), "%" + params.getNumName() + "%"));
            }
            if (null != params.getStatus()) {
                if (params.getStatus().equals(OrderStatus.FINISHED)) {
                    final CriteriaBuilder.In<Object> sectionId = criteriaBuilder.in(root.get("status"));
                    List<OrderStatus> orderStatuses = new ArrayList<>();
                    orderStatuses.add(OrderStatus.FINISHED);
                    orderStatuses.add(OrderStatus.UNFINISHED);
                    for (OrderStatus section : orderStatuses) {
                        sectionId.value(section);
                    }
                    predicates.add(sectionId);
                } else {
                    predicates.add(criteriaBuilder.equal(root.get("status"), params.getStatus()));
                }

            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        List<Sort.Order> netOrders = new ArrayList<>();
        netOrders.add(new Sort.Order(Sort.Direction.DESC, "refundTime"));
        netOrders.add(new Sort.Order(Sort.Direction.DESC, "id"));
        //查询包含系统网费的
        Page<LeaseProductOrder> orders = leaseProductOrderDao.findAll(netSpec, new PageRequest(params.getPage(), params.getSize(), new Sort(netOrders)));
        for (LeaseProductOrder order : orders.getContent()) {
            if (!order.getStatus().equals(OrderStatus.REFUNDED)) {
                if (order.getTotalAmount() > 0) {
                    BillRule billRule = billRuleDao.findOne(order.getRuleId());
                    if (billRule != null) {
                        int min = TimeUtils.getMinTimeDifference(order.getAddTime().getTime(), new Date().getTime());
                        double billRuleAmount = TimeUtils.getBillRuleAmount(billRule, min / 60, min % 60);
                        order.setPayAmount(billRuleAmount);
                        double v = order.getTotalAmount() - order.getPayAmount();
                        if (v < 0) {
                            order.setRefundAmount(0);
                            order.setGetAmount(order.getTotalAmount());
                        } else {
                            order.setRefundAmount(v);
                            order.setGetAmount(order.getPayAmount());
                        }
                    }
                }
            }
        }
        Map map = new HashMap();
        map.put("data", orders.getContent());
        map.put("totalElements", orders.getTotalElements());
        map.put("totalPages", orders.getTotalPages());
        return new ResponseEntity(map, HttpStatus.OK);
    }

    @PostMapping("/change/lease/order")
    public ResponseEntity changeLeaseOrder(@RequestBody LeaseProductOrderParams params) {
        LeaseProductOrder order = leaseProductOrderDao.findByNumNameLikeAndProductIdAndStatusInOrderByIdDesc(params.getOriNumName(), params.getProductId(), Arrays.asList(OrderStatus.FINISHED, OrderStatus.UNFINISHED));
        if (order == null) {
            return ResponseEntity.badRequest().body("订单不存在，请重新选择");
        }
        LeaseProduct product = leaseProductService.findOne(params.getProductId());
        if (product == null) {
            return ResponseEntity.badRequest().body("商品不存在，请重新选择");
        }
        product.getNumName().remove(params.getOriNumName());
        product.getNumName().add(params.getNumName());
        product.setUpdateTime(new Date());
        order.setNumName(params.getNumName());
        order.setNumId(params.getNumId());
        leaseProductService.save(product);
        leaseProductOrderDao.save(order);
        return ResponseEntity.ok("操作成功");

    }

    @GetMapping("/deal/inv/type")
    public ResponseEntity dealInvType() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        List<String> skuId = stockTransactionDao.findAllByAddTimeBetweenAndStockLevelGroupBySkuIdOrderByAddTimeDesc();
        for (String s : skuId) {
            Inventory inventory = inventoryDao.findById(Long.valueOf(s));
            StockChangeTransaction stockChangeTransaction = stockTransactionDao.findTop1BySkuIdOrderByAddTimeDesc(inventory.getId() + "");
            if (stockChangeTransaction != null && inventory.getQuantity() != stockChangeTransaction.getAllowance()) {
                inventory.setQuantity(stockChangeTransaction.getAllowance());
                inventoryDao.save(inventory);
            }
        }
        return ResponseEntity.ok("操作成功");

    }


    @PostMapping("/create/lease/order")
    public ResponseEntity createLeaseOrder(@RequestBody LeaseProductOrderParams params) throws Exception {
        return (ResponseEntity) redisSyncLocker.idLock(params.getProductId() + "leaseLock", o -> {
            LeaseProduct product = leaseProductService.findOne(params.getProductId());
            if (product == null) {
                return ResponseEntity.badRequest().body("商品不存在，请重新选择");
            }
            if (!product.isBuyEnabled() && params.getSource().equals(OrderSource.WEB)) {
                return ResponseEntity.badRequest().body("商品暂不支持WEB点单");
            }
            if (product.getSalesCount() < 0) {
                return ResponseEntity.badRequest().body(product.getName() + "库存不足,请刷新后重试");
            }
            if (product.getTotalCount() - product.getSalesCount() - 1 < 0) {
                return ResponseEntity.badRequest().body(product.getName() + "库存不足,请刷新后重试");
            }
            BillRule billRule = billRuleDao.findOne(params.getRuleId());
            if (billRule == null) {
                return ResponseEntity.badRequest().body("规则不存在，请刷新后重试");
            }
            List<OrderStatus> orderStatuses = new ArrayList<>();
            orderStatuses.add(OrderStatus.FINISHED);
            orderStatuses.add(OrderStatus.UNFINISHED);
            LeaseProductOrder joinOrder = leaseProductOrderDao.findByStoreIdAndNumIdAndStatusInAndProductId(params.getStoreId(), params.getNumId(), orderStatuses, params.getProductId());
            if (joinOrder != null) {
                return ResponseEntity.badRequest().body("已租赁，请归还后再操作，谢谢");
            }
            LeaseProductExtra extra = product.getExtra(params.getRuleId());
            if (extra == null) {
                return ResponseEntity.badRequest().body("该商品异常，请联系管理员");
            }
            StorePay storePay = storePayDao.findByStoreIdAndStartEnabled(product.getStoreId(), true);
            if (storePay == null || StringUtils.isBlank(storePay.getMchId())) {
                return ResponseEntity.badRequest().body("请联系管理员配置支付方式");
            }
            LeaseProductOrder order = new LeaseProductOrder();
            BeanUtils.copyProperties(params, order);
            order.setProductName(product.getName());
            order.setTotalAmount(extra.getAmount());
            order.setStatus(OrderStatus.INIT);
            order.setProductTypeId(product.getProductTypeId());
            order.setProductTypeName(product.getProductTypeName());
            if (extra.getAmount() == 0) {
                if (params.getSource().equals(OrderSource.POS)) {
                    order.setStatus(OrderStatus.FINISHED);
                } else {
                    order.setStatus(OrderStatus.UNFINISHED);
                }
            }
            order.setRuleId(billRule.getId());
            order.setRuleName(billRule.getName());
            LeaseProductOrder save = leaseProductOrderDao.save(order);
            LeaseProductOrderPayResult payResult = new LeaseProductOrderPayResult();
            payResult.setLeaseProductOrder(save);
            if (extra.getAmount() > 0) {
                try {
                    switch (params.getSource()) {
                        case EMPLOYEE:
                        case POS:
                            AllPayBaseResult result = suiXinFuService.suiXinFuPayFrozen(save, params.getAuthCode(), storePay.getMchId(), product);
                            if (result.isSuccess()) {
                                if (save.getStatus().equals(OrderStatus.REFUNDED)) {
                                    return ResponseEntity.badRequest().body(product.getName() + "库存不足,请刷新后重试");
                                } else {
                                    payResult.setResult(result);
                                    return ResponseEntity.ok(payResult);
                                }
                            } else {
                                return ResponseEntity.badRequest().body(result.getErrMsg());
                            }
                        case WEB:
                            ALLPayQrCodeResult qeResult = suiXinFuService.suixinfuOrderQRCodeFrozen(storePay, save);
                            if (qeResult.isSuccess()) {
                                payResult.setQeResult(qeResult);
                                return ResponseEntity.ok(payResult);
                            } else {
                                return ResponseEntity.badRequest().body(qeResult.getErrMsg());
                            }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("订单号{},异常信息{}", save.getOrderNum(), e);
                }
            } else {
                try {
                    boolean inventory = suiXinFuService.dealLeaseProductInventory(product, order.getNumName(), save);
                    if (inventory && !order.getSource().equals(OrderSource.POS)) {
                        notifyPOSNewOrderArrivalLease(order);
                    }
                    leaseProductOrderDao.save(save);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return new ResponseEntity(payResult, HttpStatus.OK);
        });

    }

    /**
     * 编辑/移除/添加原料
     *
     * @param params
     * @return
     */
    @PostMapping("/lease/product/list")
    public ResponseEntity productList(@RequestBody LeaseProductParams params) {
        List<LeaseProduct> result = leaseProductService.findByStoreIdOrderByProductTypeIdDesc(params.getStoreId());
        return ResponseEntity.ok(result);
    }

    @GetMapping("/mySelf/refund")
    public ResponseEntity myselfRefund(@RequestParam("mchId") String mchId, @RequestParam("orderNum") String orderNum, @RequestParam("amount") String amount) {
        try {
            suiXinFuService.suixinfuRefund("REF" + orderNum, mchId, Double.valueOf(amount), orderNum);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseEntity.ok("操作成功");
    }

    @PostMapping("/create/lease/order/refund")
    public ResponseEntity leaseOrderRefund(@RequestBody LeaseProductOrderParams params) throws Exception {
        return (ResponseEntity) redisSyncLocker.idLock(params.getProductId() + "leaseLock", o -> {
            LeaseProductOrder order = leaseProductOrderDao.findOne(params.getId());
            if (order == null) {
                return ResponseEntity.badRequest().body("订单不存在，请刷新后重试");
            }
            if (order.getStatus().equals(OrderStatus.REFUNDED)) {
                return ResponseEntity.badRequest().body("该订单已归还，请不要重复操作");
            }
            BillRule billRule = billRuleDao.findOne(order.getRuleId());
            if (billRule == null) {
                return ResponseEntity.badRequest().body("规则不存在，请刷新后重试");
            }
            StorePay storePay = storePayDao.findByStoreIdAndStartEnabled(order.getStoreId(), true);
            if (storePay == null) {
                return ResponseEntity.badRequest().body("请联系管理员配置支付方式");
            }
            int min = TimeUtils.getMinTimeDifference(order.getAddTime().getTime(), new Date().getTime());
            double billRuleAmount = TimeUtils.getBillRuleAmount(billRule, min / 60, min % 60);
            order.setPayAmount(billRuleAmount);
            double v = order.getTotalAmount() - order.getPayAmount();
            if (v < 0) {
                order.setRefundAmount(0);
                order.setGetAmount(order.getTotalAmount());
            } else {
                order.setRefundAmount(v);
                order.setGetAmount(order.getPayAmount());
            }
            if (params.isRefundHandEnabled()){
                if (params.getRefundAmount()>order.getRefundAmount()){
                    return ResponseEntity.badRequest().body("退款金额应小于系统计算金额");
                }else {
                    order.setRefundAmount(params.getRefundAmount());
                    order.setGetAmount(order.getTotalAmount()-params.getRefundAmount());
                }
            }
            order.setReturnEmpName(params.getReturnEmpName());
            order.setStatus(OrderStatus.REFUNDED);
            order.setRefundTime(new Date());
            order.setRefundOrderNum("REF" + order.getOrderNum() + RandomStringUtils.randomNumeric(3));
            try {
                if (order.getRefundAmount() > 0) {
                    SwiftPassRefundResult swiftPassRefundResult = suiXinFuService.suixinfuRefund(order.getRefundOrderNum(), storePay.getMchId(), order.getRefundAmount(), order.getOrderNum());
                    if (swiftPassRefundResult.getResultCode().equals("1")) {
                        return ResponseEntity.badRequest().body(swiftPassRefundResult.getErrMsg());
                    }
                }
                dealLeaseProductInventory(order.getProductId(), order.getGetAmount(), order.getNumName());
                suiXinFuService.suixinfuOrderQRCodeThaw(storePay.getMchId(), order);
            } catch (Exception e) {
                e.printStackTrace();
                return ResponseEntity.badRequest().body("退款失败");
            }
            leaseProductOrderDao.save(order);
            return ResponseEntity.ok().body(order);
        });
    }

    @GetMapping("lease/printing")
    public ResponseEntity leasePrinting(@RequestParam(value = "id", required = false) String id) {
        BeanPropertyRowMapper item = new BeanPropertyRowMapper<>(SalesLeaseItemDetail.class);
        item.setPrimitivesDefaultedForNullValue(true);
        Map<String, Object> map = new HashMap<>();
        List<SalesLeaseItemDetails> details = new ArrayList<>();
        WorkShift workShift = workShiftDao.findOne(id);
        List<SalesLeaseItemDetail> nowSalesItemDetails = jdbc.query("SELECT sum(get_amount) as amount,COUNT(id) as qty   ,product_name as name, product_type_name as typeName FROM lease_product_order WHERE status = 'REFUNDED'  and `store_id` =?  and `refund_time`  >=?  and `refund_time` <=? GROUP BY product_id order By amount desc "
                , item, workShift.getStoreId(), workShift.getBeginTime(), workShift.getEndTime());
        Map<String, String> stringMap = new HashMap<>();
        for (SalesLeaseItemDetail nowSalesItemDetail : nowSalesItemDetails) {
            stringMap.put(nowSalesItemDetail.getTypeName(), nowSalesItemDetail.getTypeName());

        }
        for (String s : stringMap.keySet()) {
            int total = 0;
            double amount = 0;
            SalesLeaseItemDetails itemDetails = new SalesLeaseItemDetails();
            for (SalesLeaseItemDetail nowSalesItemDetail : nowSalesItemDetails) {
                if (s.equals(nowSalesItemDetail.getTypeName())) {
                    SalesLeaseItemDetails.SalesLeaseItem nowItem = new SalesLeaseItemDetails.SalesLeaseItem();
                    total += nowSalesItemDetail.getQty();
                    amount += nowSalesItemDetail.getAmount();
                    nowItem.setName(nowSalesItemDetail.getName());
                    nowItem.setAmount(nowSalesItemDetail.getAmount());
                    nowItem.setQty(nowSalesItemDetail.getQty());
                    itemDetails.getLeaseItemList().add(nowItem);
                }
            }
            itemDetails.setTypeName(s);
            itemDetails.setAmount(amount);
            itemDetails.setQty(total);
            details.add(itemDetails);
        }
        map.put("printing", details);
        return ResponseEntity.ok(map);
    }


    @PostMapping("/order/by/id")
    public ResponseEntity orderById(@RequestBody LeaseProductOrderParams params) {
        List<OrderStatus> orderStatuses = new ArrayList<>();
        orderStatuses.add(OrderStatus.FINISHED);
        orderStatuses.add(OrderStatus.UNFINISHED);
        List<LeaseProductOrder> orders = leaseProductOrderDao.findByNumIdAndStatusInAndStoreIdOrderByIdDesc(params.getNumId(), orderStatuses, params.getStoreId());
        for (LeaseProductOrder order : orders) {
            if (!order.getStatus().equals(OrderStatus.REFUNDED)) {
                if (order.getTotalAmount() > 0) {
                    BillRule billRule = billRuleDao.findOne(order.getRuleId());
                    if (billRule != null) {
                        int min = TimeUtils.getMinTimeDifference(order.getAddTime().getTime(), new Date().getTime());
                        double billRuleAmount = TimeUtils.getBillRuleAmount(billRule, min / 60, min % 60);
                        order.setPayAmount(billRuleAmount);
                        double v = order.getTotalAmount() - order.getPayAmount();
                        if (v < 0) {
                            order.setRefundAmount(0);
                            order.setGetAmount(order.getTotalAmount());
                        } else {
                            order.setRefundAmount(v);
                            order.setGetAmount(order.getPayAmount());
                        }
                    }
                }
            }
        }
        return ResponseEntity.ok().body(orders);
    }


    @PostMapping("/order/detail")
    public ResponseEntity orderDetail(@RequestBody LeaseProductOrderParams params) {
        LeaseProductOrder orders = leaseProductOrderDao.findOne(params.getId());
        return ResponseEntity.ok().body(orders);


    }

    public void dealLeaseProductInventory(int productId, double amount, String name) throws Exception {
        LeaseProduct leaseProduct = leaseProductService.findOne(productId);
        if (leaseProduct != null) {
            leaseProduct.setProfitAmount(leaseProduct.getProfitAmount() + amount);
            leaseProduct.getNumName().remove(name);
            leaseProduct.setSalesCount(leaseProduct.getNumName().size());
            leaseProductService.save(leaseProduct);
        }
    }

    private void notifyPOSNewOrderArrivalLease(LeaseProductOrder order) {
        try {
            Map<String, String> map = new HashMap<>();
            map.put("orderId", order.getId() + "");
            map.put("storeId", order.getStoreId());
            map.put("type", "LEASE");
            putWs(map);
        } catch (Exception e) {
            log.warn("Fail to send websocket notify to store:{}", order.getStoreId());
        }
    }

    private void putWs(Map<String, String> map) {
        try {
            //发送ws
            String url = urlUtils.getWs();
            OkHttpUtils.post(url, com.alibaba.fastjson.JSONObject.toJSONString(map));
        } catch (Exception ex) {
            log.error(" Invitation code initialization failed msg:{}", ex);
        }

    }
}
